package both.map.mapModel.unitTests;

import org.junit.Before;
import org.junit.Test;

import both.map.LayerType;
import both.map.TerrainType;
import both.map.mapModel.ModelActor;
import both.map.mapModel.ModelLayer;
import both.map.mapModel.TerrainCell;
import both.map.mapModel.TokenCell;
import both.map.mapModel.VisibilityCell;

import java.awt.Dimension;

import junit.framework.Assert;

public class ModelLayerTest {
	private static final Dimension layerSize = new Dimension(20,20);
	private static final TerrainType em = TerrainType.empty;
	private static final TerrainType sf = TerrainType.stoneFloor;
	private static final TerrainType sw = TerrainType.stoneWall;
	private static final TerrainType gs = TerrainType.grass;
	private static final TerrainType di = TerrainType.dirt;
	private static final TerrainType co = TerrainType.cobble;
	private static final TerrainType sa = TerrainType.sand;
	private static final TerrainType gv = TerrainType.gravel;
	private static final TerrainType mu = TerrainType.mud;
	private static final TerrainType wa = TerrainType.water;
	
	private static final TerrainType[][] tLayer1 =
	{  // 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19	
		{em,em,em,em,em,em,em,em,em,em,em,em,em,em,em,em,em,em,em,em}, //0
		{sw,sw,sw,sw,sw,sw,sw,sw,gs,gs,gs,gs,gs,gs,gs,gs,gs,sa,sa,sa}, //1
		{sw,di,di,di,di,di,di,sw,gs,gs,gs,gs,gs,gs,gs,gs,sa,wa,wa,wa}, //2
		{sw,di,di,di,di,di,di,sw,gs,gs,mu,mu,mu,gs,gs,sa,wa,wa,wa,wa}, //3
		{sw,di,di,di,di,di,di,sw,gs,gs,mu,wa,mu,gs,gs,sa,wa,wa,wa,wa}, //4
		{sw,di,di,di,di,di,di,sw,gs,gs,mu,mu,mu,gs,gs,sa,sa,wa,wa,wa}, //5
		{sw,di,di,di,di,di,di,di,di,di,di,di,di,di,gs,sa,sa,wa,wa,wa}, //6
		{sw,di,di,di,di,di,di,sw,gs,gs,gs,gs,gs,di,gs,sa,wa,wa,wa,wa}, //7
		{sw,di,di,di,di,di,di,sw,gs,gs,gs,gs,gs,di,gs,wa,wa,wa,wa,wa}, //8
		{sw,sw,sw,sw,sw,sw,sw,sw,gs,gs,gs,gs,gs,di,gs,wa,sa,gs,wa,wa}, //9
		{gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,di,gs,wa,gs,sa,sa,sa}, //10
		{gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,di,gs,wa,gs,gs,gs,gs}, //11
		{sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf}, //12
		{sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf}, //13
		{sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf}, //14
		{sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf,sf}, //15
		{gs,gs,co,co,co,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,wa,gs,gs,gs,gs}, //16
		{gs,gs,co,co,co,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,wa,gs,gs,gv,gv}, //17
		{gs,gs,co,co,co,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,wa,gs,gv,gv,gv}, //18
		{gs,gs,co,co,co,gs,gs,gs,gs,gs,gs,gs,gs,gs,gs,wa,gs,gv,gv,gv} //19
	};
	
	private static final boolean[] tt = {true,true};
	private static final boolean[] ft = {false,true};
	private static final boolean[] ff = {false,false};
	private static final boolean[][][] vLayer1 =
	{  // 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}, //0
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}, //1
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}, //2
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //3
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //4
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //5
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //6
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //7
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff,ff}, //8
		{ff,ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,ft,ft,ft,ft,ft,ff,ff,ff,ff}, //9
		{ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,tt,tt,tt,tt,tt,ft,ft,ff,ff,ff}, //10
		{ff,ff,ff,ff,ff,ff,ff,ft,ft,ft,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //11
		{ff,ft,ft,ft,ft,ft,ft,tt,tt,tt,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //12
		{ff,ft,ft,ft,ft,ft,ft,tt,tt,tt,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //13
		{ff,ft,ft,ft,ft,ft,ft,tt,tt,tt,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //14
		{ff,ff,ff,ff,ff,ff,ff,tt,tt,tt,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //15
		{ff,ff,ff,ff,ff,ff,ff,tt,tt,tt,tt,tt,tt,tt,tt,tt,ft,ff,ff,ff}, //16
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}, //17
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}, //18
		{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff} //19
	};
	
	private static final ModelActor _a = new ModelActor(new both.character.CharacterStore());
	private static final ModelActor _b = new ModelActor(new both.character.CharacterStore());
	private static final ModelActor _c = new ModelActor(new both.character.CharacterStore());
	private static final ModelActor no = null;
	private static final ModelActor[][] aLayer1 = 
	{  // 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //0
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //1
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //2
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //3
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //4
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //5
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //6
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //7
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //8
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //9
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //10
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //11
		{no,no,no,no,no,no,no,no,no,no,no,no,_a,no,no,no,no,no,no,no}, //12
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //13
		{no,no,no,no,no,no,no,no,no,no,_c,no,no,_b,no,no,no,no,no,no}, //14
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //15
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //16
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //17
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no}, //18
		{no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no} //19
	};
	
	private ModelLayer visibilityLayer;
	private ModelLayer tokenLayer;
	private ModelLayer terrainLayer;
		
	@Before
	public void setUp() throws Exception 
	{
		makeLayers();
	}
	
	private void makeLayers()
	{
		visibilityLayer = new ModelLayer(LayerType.VISIBILITY, layerSize);
		tokenLayer = new ModelLayer(LayerType.TOKEN, layerSize);
		terrainLayer = new ModelLayer(LayerType.TERRAIN, layerSize);
		
		for (int x=0; x<layerSize.width; x++){ 
			for (int y=0; y<layerSize.height; y++) {
				VisibilityCell v = (VisibilityCell) visibilityLayer.getCell(x,y);
				v.setVisibility(vLayer1[x][y][0], vLayer1[x][y][1]);
				
				TokenCell to = (TokenCell) tokenLayer.getCell(x,y);
				to.addActor(aLayer1[x][y]);
				
				TerrainCell te = (TerrainCell) terrainLayer.getCell(x,y);
				te.reassignTerrain(tLayer1[x][y]);
			}
		}
		visibilityLayer.grabUpdate();
		terrainLayer.grabUpdate();
		tokenLayer.grabUpdate();
	}

	@Test
	public void testGetSize() {
		Assert.assertEquals(new Dimension(10,5),
							new ModelLayer(LayerType.TERRAIN, new Dimension(10,5)).getSize());
		Assert.assertEquals(new Dimension(20,20), tokenLayer.getSize());
	}

	@Test
	public void testGetCellNormal() {
		for (int x=0; x<layerSize.width; x++) {
			for (int y=0; y<layerSize.height; y++) {
				VisibilityCell vc = (VisibilityCell) visibilityLayer.getCell(x,y);
				Assert.assertEquals(vLayer1[x][y][0], vc.hasBeenSeen());
				Assert.assertEquals(vLayer1[x][y][1], vc.isCurrentlyVisible());
				vc.setVisibility(true,true);
				VisibilityCell vcu = (VisibilityCell) visibilityLayer.getCell(x,y);
				Assert.assertEquals(vc, vcu);
			}
		}
	}
	
	@Test
	public void testGetCellOutOfBounds()
	{
		try {
			visibilityLayer.getCell(-1, 2);
			Assert.fail("Exception not thrown on getCellOutOfBounds visibility [-1,2]");
		} catch (ArrayIndexOutOfBoundsException e) {
		}
		try {
			tokenLayer.getCell(30, 2);
			Assert.fail("Exception not thrown on getCellOutOfBounds token [30,2]");
		} catch (ArrayIndexOutOfBoundsException e) {
		}
		try {
			terrainLayer.getCell(-1, 2);
			Assert.fail("Exception not thrown on getCellOutOfBounds terrain [0,21]");
		} catch (ArrayIndexOutOfBoundsException e) {
		}
		try {
			visibilityLayer.getCell(1,2);
		} catch (ArrayIndexOutOfBoundsException e) {
			Assert.fail("Exception thrown on getCell visibility [1,2]");
		}
	}

	@Test
	public void testUpdate() {
		visibilityLayer.grabUpdate();
		tokenLayer.grabUpdate();
		terrainLayer.grabUpdate();
		Assert.assertNull(visibilityLayer.grabUpdate());
		Assert.assertNull(tokenLayer.grabUpdate());
		Assert.assertNull(terrainLayer.grabUpdate());
		
		VisibilityCell vc = (VisibilityCell) visibilityLayer.getCell(2,3);
		vc.setVisibility(true,true);
		
		TerrainCell tc = (TerrainCell) terrainLayer.getCell(5,17);
		tc.reassignTerrain(TerrainType.water);
		
		ModelLayer updateVis = visibilityLayer.grabUpdate();
		ModelLayer updateTer = terrainLayer.grabUpdate();
		ModelLayer updateTok = tokenLayer.grabUpdate();
		
		Assert.assertNotNull(updateVis);
		Assert.assertNotNull(updateTer);
		Assert.assertNull(updateTok);
		
		ModelLayer recipientVis = new ModelLayer(LayerType.VISIBILITY, layerSize);
		ModelLayer recipientTer = new ModelLayer(LayerType.TERRAIN, layerSize);
		ModelLayer recipientTok = new ModelLayer(LayerType.TOKEN, layerSize);
		
		recipientVis.applyUpdate(updateVis);
		recipientTer.applyUpdate(updateTer);
		recipientTok.applyUpdate(updateTok);
		
		// make sure the update applied
		VisibilityCell recVis = (VisibilityCell) recipientVis.getCell(2,3);
		Assert.assertTrue(recVis.hasBeenSeen());
		Assert.assertTrue(recVis.isCurrentlyVisible());
		// make sure it didn't apply to anything not updated
		recVis = (VisibilityCell) recipientVis.getCell(5,5);
		Assert.assertFalse(recVis.hasBeenSeen());
		Assert.assertFalse(recVis.isCurrentlyVisible());
		
		TerrainCell recTer = (TerrainCell) recipientTer.getCell(5,17);
		Assert.assertEquals(TerrainType.water, recTer.getTerrainType());
		recTer = (TerrainCell) recipientTer.getCell(6,1);
		Assert.assertEquals(TerrainType.empty, recTer.getTerrainType());
		
		// make sure updates are flagged correctly on update recipients
		Assert.assertNotNull(recipientVis.grabUpdate());
		Assert.assertNotNull(recipientTer.grabUpdate());
		Assert.assertNull(recipientTok.grabUpdate());
	}

	@Test
	public void testGetType() {
		Assert.assertEquals(LayerType.TERRAIN, terrainLayer.getType());
		Assert.assertEquals(LayerType.TOKEN, tokenLayer.getType());
		Assert.assertEquals(LayerType.VISIBILITY, visibilityLayer.getType());
	}

}
